import ij.IJ;
import ij.ImagePlus;
import ij.WindowManager;
import ij.gui.GUI;
import ij.gui.Plot;
import ij.plugin.frame.*;
import ij.process.ImageProcessor;


import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.Map.Entry;
	
public class Correlation_ extends PlugInFrame implements ActionListener {

	public static final String DISTANCE_INPUT = "Distance";
	public static final String AUTOCORRELATION = "AutoCorrelation";
	public static final String RADIAL_CORRELATION_COMMAND = "View graph";
	public static final String SPECTRUM = "Spectrum";

	public double min = 0;
	public double max = 255;
	private double corr = Double.NaN;


	private ImagePlus imp;
	private ImageProcessor ip;
	private Panel mainPane;

	private Map<String, TextField> commandFields = new TreeMap<String, TextField>();
	private Map<String, Label> results = new TreeMap<String, Label>();
	private Map<String, TextField> inputs = new TreeMap<String, TextField>();

	public Correlation_() {
		super("Autocorrelation analysis");
		imp = WindowManager.getCurrentImage();
		if (imp == null) {
			IJ.showMessage("No image", "There are no images open.");
			return;
		}

		mainPane = new Panel();
		mainPane.setLayout(new GridLayout(0, 3, 10, 5));
		addResult(AUTOCORRELATION);
		addCommand(DISTANCE_INPUT, RADIAL_CORRELATION_COMMAND, 3.0);
		add(mainPane);
		pack();
		setVisible(true);
		GUI.center(this);
	}

	public void run(java.lang.String arg) {
		if (imp == null) {
			return;
		}
		ip = imp.getProcessor();
	}

	private void addCommand(String labelText, String buttonText,
			double defaultValue) {
		Label label = new Label(labelText);
		TextField textField = new TextField(String.format("%.0f", defaultValue));
		commandFields.put(buttonText, textField);
		inputs.put(labelText, textField);
		Button button = new Button(buttonText);
		button.addActionListener(this);
		button.setActionCommand(buttonText);
		mainPane.add(label);
		mainPane.add(textField);
		mainPane.add(button);
	}

	private void addResult(String resultName) {
		Label nameLabel = new Label(resultName);
		Label resultLabel = new Label();
		results.put(resultName, resultLabel);
		mainPane.add(nameLabel);
		mainPane.add(resultLabel);
		addLabel("");
	}

	private void addLabel(String labelText) {
		mainPane.add(new Label(labelText));
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		imp = WindowManager.getCurrentImage();
		String command = e.getActionCommand();
		if (RADIAL_CORRELATION_COMMAND.equals(command)) {
			corr = (calculateCorrelation(ip, 1, 0) + calculateCorrelation(ip, 0, 1) + calculateCorrelation(ip, -1, 0) + calculateCorrelation(ip, 0, -1)) / 4;
			results.get(AUTOCORRELATION).setText(format(corr));			
			drawRadialCorrelation(ip, (int)Double.parseDouble(inputs.get(DISTANCE_INPUT).getText()));
		} 
	}

	private double calculateCorrelation(ImageProcessor ip, int n, int m) {
		byte[] mask = ip.getMaskArray();
		int width = ip.getWidth();
		int height = ip.getHeight();
		Rectangle roi = ip.getRoi();
		int rx, ry, rw, rh;
		if (roi != null) {
			rx = roi.x;
			ry = roi.y;
			rw = roi.width;
			rh = roi.height;
		} else {
			rx = 0;
			ry = 0;
			rw = width;
			rh = height;
		}
		int pixelCount = 0;
		int i, mi;
		double v, v2, sum1 = 0.0, sum2 = 0.0;
		for (int y = ry, my = 0; y < (ry + rh); y++, my++) {
			i = y * width + rx;
			mi = my * rw;
			for (int x = rx; x < (rx + rw); x++) {
				if (mask == null || mask[mi++] != 0) {
					pixelCount++;
					v = ip.getPixelValue(x, y);
					v2 = v * v;
					sum1 += v;
					sum2 += v2;
				}
				i++;
			}
		}
		double variance = sum2 / (double)pixelCount - sum1 * sum1 / (double)pixelCount / (double)pixelCount;
		if (variance > 0.0) {
			int consideredPixelCount = 0;
			double avg = sum1 / pixelCount;
			double result = 0.0;
			for (int y = ry, my = 0; y < (ry + rh); y++, my++) {
				i = y * width + rx;
				mi = my * rw;
				for (int x = rx; x < (rx + rw); x++)
					if (mask == null || mask[mi++] != 0)
						if (x + n < rx + rw && x + n >= rx && y + m < ry + rh && y + m >= ry
								&& (mask == null || mask[mi - 1 + n + m * rw] != 0)) {
							result += (ip.getPixelValue(x, y) - avg) * (ip.getPixelValue(x + n, y + m) - avg);
							consideredPixelCount++;
						}
					i++;
			}
			return result / (double)consideredPixelCount / variance;
		} else
			return 0.0;
	}

	private void drawRadialCorrelation(ImageProcessor ip, int r) {
		imp = WindowManager.getCurrentImage();
		if (imp == null) {
			IJ.showMessage("No image", "There are no images open.");
			return;
		}
		byte[] mask = ip.getMaskArray();
		int width = ip.getWidth();
		int height = ip.getHeight();
		Rectangle roi = ip.getRoi();
		int rx, ry, rw, rh;
		if (roi != null) {
			rx = roi.x;
			ry = roi.y;
			rw = roi.width;
			rh = roi.height;
		} else {
			rx = 0;
			ry = 0;
			rw = width;
			rh = height;
		}
		int pixelCount = 0;
		int i, mi;
		double v, v2, sum1 = 0.0, sum2 = 0.0;
		for (int y = ry, my = 0; y < (ry + rh); y++, my++) {
			i = y * width + rx;
			mi = my * rw;
			for (int x = rx; x < (rx + rw); x++) {
				if (mask == null || mask[mi++] != 0) {
					pixelCount++;
					v = ip.getPixelValue(x, y);
					v2 = v * v;
					sum1 += v;
					sum2 += v2;
				}
				i++;
			}
		}
		double variance = sum2 / (double)pixelCount - sum1 * sum1 / (double)pixelCount / (double)pixelCount;
		if (variance > 0.0) {
			int[][] counts = new int[r][r];
			double[][] results = new double[r][r];
			double avg = sum1 / pixelCount;
			for (int y = ry, my = 0; y < (ry + rh); y++, my++) {
				i = y * width + rx;
				mi = my * rw;
				for (int x = rx; x < (rx + rw); x++)
					if (mask == null || mask[mi++] != 0)
						for (int loci = 0; loci < r; ++loci)
							for (int locj = 0; locj < r; ++locj) {
								if (x + loci < rx + rw && x + loci >= rx && y + locj < ry + rh && y + locj >= ry
										&& (mask == null || mask[mi - 1 + loci + locj * rw] != 0)) {
									results[loci][locj] += (ip.getPixelValue(x, y) - avg) * (ip.getPixelValue(x + loci, y + locj) - avg);
									counts[loci][locj]++;
								}
							}
					i++;
			}
			double[] radialCorrelation = new double[2 * r * r];
			int[] radialCorrelationPointCounts = new int[2 * r * r];
			for (int loci = 0; loci < r; ++loci)
				for (int locj = 0; locj < r; ++locj) {
					results[loci][locj] /= (double)counts[loci][locj] * variance;
					int position = loci * loci + locj * locj;
					radialCorrelation[position] += results[loci][locj];
					radialCorrelationPointCounts[position]++;
				}
			TreeMap<Double, Double> graph = new TreeMap<Double, Double>();
			for (int cnt = 0; cnt < r * r; ++cnt)
				if (radialCorrelationPointCounts[cnt] > 0)
					graph.put(Math.sqrt(cnt), radialCorrelation[cnt] / radialCorrelationPointCounts[cnt]);
			double[] keys = new double[graph.size()];
			double[] values = new double[graph.size()];
			int cnt = 0;
			for (Entry<Double, Double> entry : graph.entrySet()) {
				keys[cnt] = entry.getKey();
				values[cnt] = entry.getValue();
				cnt++;
			}
			Plot plot = new Plot("Radial Correlation", "Distance", "Correlation", keys, values);
			plot.show();
		}
	}

	private String format(double d) {
		return String.format("%f", d);
	}
}